/*
 * Copyright � 2011 Arizona State University
 *
 * This file is part of ProMDATE.
 *
 * ProMDATE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ProMDATE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ProMDATE.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "ControlElement.h"
#include "Control.h"
#include <iostream>

ControlElement::ControlElement(std::string desc, std::string name) :
	Element() {
	setDescription(desc);
	numControls = 0;
	controlsList = new Control*[0];
	order = new int*[0];
	buttonName = name;
	pthread_rwlock_init(&controlLock, NULL);
	pthread_cond_init(&buttonCond, NULL);
	pthread_mutex_init(&buttonLock, NULL);
}

ControlElement::~ControlElement() {
	for (int i = 0; i < numControls; i++) {
		delete controlsList[i];
		delete[] order[i];
	}
	delete[] controlsList;
	delete[] order;
	pthread_rwlock_destroy(&controlLock);
	pthread_cond_destroy(&buttonCond);
	pthread_mutex_destroy(&buttonLock);
}

int ControlElement::getType() {
	return CONTROLELEMENT;
}
int ControlElement::addControl(Control* newCon) {
	pthread_rwlock_rdlock(&controlLock);
	Control** newControlsList = new Control*[numControls + 1];
	int** newOrder = new int*[numControls + 1];
	for (int i = 0; i < numControls; i++) {
		newControlsList[i] = controlsList[i];
		newOrder[i] = order[i];
	}
	newControlsList[numControls] = newCon;

	newOrder[numControls] = new int[2];
	newOrder[numControls][0] = numControls;
	newOrder[numControls][1] = newControlsList[numControls]->getType();
	pthread_rwlock_unlock(&controlLock);

	pthread_rwlock_wrlock(&controlLock);
	numControls++;
	delete[] controlsList;
	delete[] order;
	controlsList = newControlsList;
	order = newOrder;
	int returnNum = numControls - 1;
	pthread_rwlock_unlock(&controlLock);

	return returnNum;
}

int ControlElement::removeControl(int ID) {
	pthread_rwlock_rdlock(&controlLock);
	if (ID < numControls && ID > -1) {
		if (controlsList[ID] != NULL) {
			pthread_rwlock_unlock(&controlLock);
			pthread_rwlock_wrlock(&controlLock);
			delete controlsList[ID];
			controlsList[ID] = NULL;
			for (int i = 0; i < numControls; i++) {
				if (order[i][0] == ID)
					order[i][1] = REMOVED;
			}
			pthread_rwlock_unlock(&controlLock);
		} else {
			pthread_rwlock_unlock(&controlLock);
			std::cout << "Error removing Control: it has already been removed" << std::endl;
		}
		return 0;
	} else {
		pthread_rwlock_unlock(&controlLock);
		std::cout << "Error removing Control: irrational Control identifier" << std::endl;
		return -1;
	}
}

void ControlElement::setOrder(int* controls, int* newOrder, int length) {
	pthread_rwlock_wrlock(&controlLock);
	for (int i = 0; i < numControls; i++)
		order[i][0] = -1;
	for (int i = 0; i < length; i++) {
		if (controls[i] < numControls && controls[i] > -1 && newOrder[i] < numControls && newOrder[i] > -1) {
			order[newOrder[i]][0] = controls[i];
			order[newOrder[i]][1] = controlsList[order[newOrder[i]][0]]->getType();
		}
	}

	for (int i = 0; i < numControls; i++) {
		if (order[i][0] == -1) {
			bool jNotUsed = true;
			for (int j = 0; j < numControls; j++) {
				for (int k = 0; k < numControls; k++) {
					if (order[k][0] == j) {
						jNotUsed = false;
						break;
					}
				}
				if (jNotUsed) {
					order[i][0] = j;
					order[i][1] = controlsList[j]->getType();
					break;
				}
			}
		}
	}
	pthread_rwlock_unlock(&controlLock);
}

int ControlElement::getNumberControls() {
	pthread_rwlock_rdlock(&controlLock);
	int numControlsSafe = numControls;
	pthread_rwlock_unlock(&controlLock);
	return numControlsSafe;
}

Control* ControlElement::getControlFromOrder(int num, int& type) {
	pthread_rwlock_rdlock(&controlLock);
	if (num < numControls && num > -1) {
		type = order[num][1];
		pthread_rwlock_unlock(&controlLock);
		return controlsList[order[num][0]];
	} else {
		pthread_rwlock_unlock(&controlLock);
		std::cout << "Error returning Control identifier: irrational order number requested" << std::endl;
		return NULL;
	}
}

Control* ControlElement::getControl(int ID) {
	return controlsList[ID];
}

void ControlElement::setButtonName(std::string name) {
	pthread_rwlock_wrlock(&lock);
	buttonName = name;
	pthread_rwlock_unlock(&lock);
}

std::string ControlElement::getButtonName() {
	pthread_rwlock_rdlock(&lock);
	std::string buttonNameSafe = buttonName;
	pthread_rwlock_unlock(&lock);
	return buttonNameSafe;
}

pthread_cond_t* ControlElement::getButtonCond() {
	return &buttonCond;
}

pthread_mutex_t* ControlElement::getButtonLock() {
	return &buttonLock;
}
